package ads.client;

import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Set;
import java.util.SortedSet;

import org.apache.log4j.Logger;

import ads.exception.PlayingGameException;
import ads.remote.ClientInterface;
import ads.remote.PeerInterface;
import ads.util.GameInfo;
import ads.util.PlayerInfo;
import at.falb.games.alcatraz.api.Alcatraz;
import at.falb.games.alcatraz.api.MoveListener;
import at.falb.games.alcatraz.api.Player;
import at.falb.games.alcatraz.api.Prisoner;

public class GameBoard implements MoveListener {
	private static final Logger log = Logger.getLogger(GameBoard.class
			.getName());

	private int numPlayers;
	private GameInfo game;
	private String playerName;
	private Alcatraz a1;
	private int myPlayerId;
	private Move newMove;
	private boolean recovery = false;

	private static int moveID = 0;
	private static GameBoard instance;

	protected static java.util.Hashtable<Integer, Move> moveList;

	private GameBoard(GameInfo game, int myPlayerId, String playerName,
			int moveID) {
		moveList = new Hashtable<Integer, Move>();
		numPlayers = game.getCountOfPlayer();
		GameBoard.moveID = moveID;
		this.game = game;
		this.playerName = playerName;
		this.myPlayerId = myPlayerId;

		a1 = new Alcatraz();

		log.debug("no of players:" + game.getCountOfPlayer() + ", playerId:"
				+ myPlayerId + ", player name:" + playerName);
		a1.init(game.getCountOfPlayer(), myPlayerId);
		a1.getPlayer(myPlayerId).setName(playerName);

		a1.showWindow();
		a1.addMoveListener(this);
		a1.start();

		log.debug("Alcatraz started");
	}

	public static GameBoard getInstance(GameInfo game, int myPlayerId,
			String playerName, int moveID) {
		if (GameBoard.instance == null)
			GameBoard.instance = new GameBoard(game, myPlayerId, playerName,
					moveID);

		return GameBoard.instance;
	}

	/**
	 * The game entity needs to be update on the GameBoard & the previous moves
	 * need to be performed.
	 * 
	 * @param game
	 * @param recoveryMoveList
	 */
	public void doMove(GameInfo game, Hashtable<Integer, Move> recoveryMoveList) {
	//	this.game = game;	
		moveList = recoveryMoveList;
		recovery = true;
		Enumeration<Integer> e = recoveryMoveList.keys();
		Move move;
		Integer moveID;
		// for now use counter for the order, change it to make it
		// generic solution
		int counter = 1;
		while (e.hasMoreElements()) {
			
			moveID = e.nextElement();
			move = recoveryMoveList.get(counter);
//			log.debug("moveID:" + moveID + ", player:"
//					+ recoveryMoveList.get(moveID).getPlayer());

			a1.doMove(move.getPlayer(), move.getPrisoner(), move.getRowOrCol(),
					move.getRow(), move.getCol());
			counter++;
		}
		recovery = false;
	}

	public void doMove(Move move) {
//		 this.game = game;
		log.debug("game:"+game);
		int previousMoveID = move.getMoveID();
		moveID = previousMoveID + 1;
		log.debug("previous moveID:" + previousMoveID + "; new moveID:"
				+ moveID);
		a1.doMove(move.getPlayer(), move.getPrisoner(), move.getRowOrCol(),
				move.getRow(), move.getCol());
	}

	@Override
	public void doMove(Player player, Prisoner prisoner, int rowOrCol, int row,
			int col) {

		System.out.println("moving " + prisoner + " to "
				+ (rowOrCol == Alcatraz.ROW ? "row" : "col") + " "
				+ (rowOrCol == Alcatraz.ROW ? row : col));

		/*
		 * for (int i = 0; i < numPlayers - 1; i++) {
		 * other[i].doMove(other[i].getPlayer(player.getId()), other[i]
		 * .getPrisoner(prisoner.getId()), rowOrCol, row, col); }
		 */

		// if recovery is happening on a player do not add to moveList &
		// do not send to other players
		if (!recovery) {
			newMove = new Move(moveID, player, prisoner, rowOrCol, row, col);
			// add my move to moveList
			moveList.put(moveID, newMove);

			// send move to all players
			log.debug("game:"+game);
			ArrayList<PlayerInfo> playersInfo = game.getPlayers();
			Iterator<PlayerInfo> i = playersInfo.iterator();
			PlayerInfo playerInfo;

			PeerInterface otherPlayer;
			boolean moveDelivered = true;
			while (i.hasNext()) {
				playerInfo = i.next();
				if (!playerName.equals(playerInfo.getPlayer().getName())) {
					otherPlayer = (PeerInterface) playerInfo.getStub();
					log.debug("move with moveID:" + newMove.getMoveID()
							+ " sent to:" + playerInfo.getPlayer() + "stub:"
							+ playerInfo.getStub());
					try {
						otherPlayer.sendMove(newMove);
					} catch (RemoteException e) {
						// TODO Auto-generated catch block
						// e.printStackTrace();
						log
								.error("Exception while calling remote method sendMove:"
										+ e);
						moveDelivered = false;

					}
					if (!moveDelivered) {
						// start timeout, redeliver?
					}

					moveDelivered = true;
				}
			}// while
		}// if
		recovery = false;
	}

	@Override
	public void gameWon(Player player) {
		// TODO Auto-generated method stub
		System.out.println("Player " + player.getId() + " wins.");

	}

	@Override
	public void undoMove() {
		// TODO Auto-generated method stub

	}

	public Move getNewMove() {
		return newMove;
	}
	
	public  void updateGameInfo(GameInfo game){
		this.game = game;
	}

}
